#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "LockGuard.hpp"


#define CAP_NUM 5

template<class T>
class BlockQueue
{
public:
    BlockQueue(size_t capacity = CAP_NUM) :_capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_Empty, nullptr);
        pthread_cond_init(&_Full, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Empty);
        pthread_cond_destroy(&_Full);
    }

    void push(const T& in) // 生产者
    {
        // 加锁
        LockGuard lock(&_mtx);
        // pthread_mutex_lock(&_mtx);
        // 对临界资源的判断
        // 如果满了，就要条件等待，_Full
        while(IsFull()) pthread_cond_wait(&_Full, &_mtx);
        // 访问临界资源
        _queue.push(in);
        // _Empty条件满足了，生产了资源，唤醒_Empty
        pthread_cond_signal(&_Empty);
        // 解锁
        // pthread_mutex_unlock(&_mtx);
    }// LockGuard 自动析构解锁

    void pop(T* out) // 消费者
    {
        // 加锁
        LockGuard lock(&_mtx);
        // pthread_mutex_lock(&_mtx);
        // 对临界资源的判断
        while(IsEmpty()) pthread_cond_wait(&_Empty, &_mtx);
        // 访问临界资源
        *out = _queue.front();
        _queue.pop();
        // Full条件满足了，不是满的了，就可以按照一定的规则决定是否唤醒_Full
        pthread_cond_signal(&_Full);
        // 解锁
        // pthread_mutex_unlock(&_mtx);
    }// LockGuard 自动析构解锁

private:
    bool IsEmpty() { return _queue.size() == 0; }
    bool IsFull() { return _queue.size() == _capacity; }

private:
    std::queue<T> _queue;
    size_t _capacity;
    pthread_mutex_t _mtx;
    pthread_cond_t _Empty;
    pthread_cond_t _Full;
};